﻿using System;
using System.ComponentModel;
using System.Linq;
using Loki;
using Loki.Commands;
using Loki.UI;
using Luna.Model.Accounting;

namespace Luna.UI.Accounting
{
    public class AccountDetailViewModel : LunaViewModel
    {
        #region Properties
        #region  StartDate
        private static PropertyChangedEventArgs _StartDateChangeArgs = ObservableHelper.CreateArgs<AccountDetailViewModel>(x => x.StartDate);

        private DateTime _StartDate;

        public DateTime StartDate
        {
            get
            {
                return _StartDate;
            }

            set
            {
                if (value != _StartDate)
                {
                    _StartDate = value;
                    NotifyChangedAndDirty(_StartDateChangeArgs);
                }
            }
        }
        #endregion

        #region  InitialAmount
        private static PropertyChangedEventArgs _InitialAmountChangeArgs = ObservableHelper.CreateArgs<AccountDetailViewModel>(x => x.InitialAmount);

        private decimal _InitialAmount;

        public decimal InitialAmount
        {
            get
            {
                return _InitialAmount;
            }

            set
            {
                if (value != _InitialAmount)
                {
                    _InitialAmount = value;
                    NotifyChangedAndDirty(_InitialAmountChangeArgs);
                }
            }
        }
        #endregion

        #region  Name
        private static PropertyChangedEventArgs _NameChangeArgs = ObservableHelper.CreateArgs<AccountDetailViewModel>(x => x.Name);

        private string _Name;

        public string Name
        {
            get
            {
                return _Name;
            }

            set
            {
                if (value != _Name)
                {
                    _Name = value;
                    NotifyChangedAndDirty(_NameChangeArgs);
                }
            }
        }
        #endregion

        #region  Bank
        private static PropertyChangedEventArgs _BankChangeArgs = ObservableHelper.CreateArgs<AccountDetailViewModel>(x => x.Bank);

        private string _Bank;

        public string Bank
        {
            get
            {
                return _Bank;
            }

            set
            {
                if (value != _Bank)
                {
                    _Bank = value;
                    NotifyChangedAndDirty(_BankChangeArgs);
                }
            }
        }
        #endregion

        #region  Number
        private static PropertyChangedEventArgs _NumberChangeArgs = ObservableHelper.CreateArgs<AccountDetailViewModel>(x => x.Number);

        private string _Number;

        public string Number
        {
            get
            {
                return _Number;
            }

            set
            {
                if (value != _Number)
                {
                    _Number = value;
                    NotifyChangedAndDirty(_NumberChangeArgs);
                }
            }
        }
        #endregion

        public LokiCollection<DisplayableOperation> Operations { get; private set; }

        public LokiCollection<DisplayNamedEntity> SourceCategories { get; private set; }

        public LokiCollection<DisplayNamedEntity> SourceProjects { get; private set; }
        #endregion

        public IEditableAccount CurrentAccount { get; set; }

        public ICommand AddLine { get; private set; }

        public AccountDetailViewModel()
        {
            Operations = CreateTrackedCollection<DisplayableOperation>();
            SourceCategories = CreateCollection<DisplayNamedEntity>();
            SourceProjects = CreateCollection<DisplayNamedEntity>();

            Init += AccountDetailViewModel_Init;
            Load += AccountDetailViewModel_Load;
        }

        private void AccountDetailViewModel_Init(object P_Sender, EventArgs e)
        {
            Commands.Handle(ApplicationCommands.SAVE, Application_Save_CanExecute, Application_Save_Execute);
        }

        private void AccountDetailViewModel_Load(object P_Sender, EventArgs e)
        {
            Synchronizer.FromAccountingCategories(SourceCategories);
            Synchronizer.FromProjects(SourceProjects);

            LoadValues(CurrentAccount);
        }

        #region Application save
        private void Application_Save_CanExecute(object P_Sender, CanExecuteCommandEventArgs e)
        {
            e.CanExecute |= IsDirty;
        }

        private void Application_Save_Execute(object P_Sender, CommandEventArgs e)
        {
            RequiresValidation();
            if (IsValid)
            {
                try
                {
                    CollectValues(CurrentAccount);
                    ApplicationModel.Accounting.Save(CurrentAccount);

                    // contre validation
                    CurrentAccount = ApplicationModel.Accounting.Accounts[CurrentAccount.ID].EditableVersion;
                    LoadValues(CurrentAccount);

                    AcceptChanges();
                    UserMessage(Messages.UIMessages.SAVE_OK);
                }
                catch (Exception Ex)
                {
                    HandleException(Ex);
                }
            }
        }
        #endregion

        protected override bool ValidateData(ValidationMode P_Mode)
        {
            bool L_IsValid = !string.IsNullOrWhiteSpace(Name)
                 && !string.IsNullOrWhiteSpace(Bank)
                 && !string.IsNullOrWhiteSpace(Number);

            foreach (var L_Operation in Operations)
            {
                L_IsValid &= L_Operation.CategoryID.HasValue;
            }

            return L_IsValid;
        }

        private void CollectValues(IEditableAccount P_ToSave)
        {
            P_ToSave.Name = Name;
            P_ToSave.Bank = Bank;
            P_ToSave.Number = Number;
            P_ToSave.InitialAmount = InitialAmount;
            P_ToSave.StartDate = StartDate;

            foreach (var L_RemovedOperation in Operations.RemovedItems)
            {
                IAccountOperation L_OldOperation = P_ToSave.Operations.FirstOrDefault(x => x.ID == L_RemovedOperation.ID);
                if (L_OldOperation != null)
                {
                    P_ToSave.RemoveOperation(L_OldOperation);
                }
            }

            foreach (var L_AddOrNew in Operations.Where(x => x.IsDirty))
            {
                IAccountOperation L_OldOperation = P_ToSave.Operations.FirstOrDefault(x => x.ID == L_AddOrNew.ID);
                if (L_OldOperation == null)
                {
                    L_OldOperation = ApplicationModel.Accounting.Create<IAccountOperation>();
                }

                L_OldOperation.Amount = L_AddOrNew.Amount;
                L_OldOperation.Category = ApplicationModel.Accounting.Categories[L_AddOrNew.CategoryID.Value];
                L_OldOperation.Description = L_AddOrNew.Description;
                L_OldOperation.ExecutionDate = L_AddOrNew.Date;
                L_OldOperation.Project = L_AddOrNew.ProjectID == null ? null : ApplicationModel.Project.Projects[L_AddOrNew.ProjectID.Value];
                L_OldOperation.OperationType = AccountOperationType.Others;
                L_OldOperation.ValueDate = L_AddOrNew.ValueDate;
                P_ToSave.AddOperation(L_OldOperation);
            }
        }

        private void LoadValues(IAccount P_ToLoad)
        {
            Name = P_ToLoad.Name;
            Bank = P_ToLoad.Bank;
            Number = P_ToLoad.Number;
            InitialAmount = P_ToLoad.InitialAmount;
            StartDate = P_ToLoad.StartDate;

            Operations.Clear();
            Operations.RemovedItems.Clear();

            foreach (var L_Operation in P_ToLoad.Operations)
            {
                DisplayableOperation L_DOperation = new DisplayableOperation();
                L_DOperation.BeginInit();
                L_DOperation.Amount = L_Operation.Amount;
                L_DOperation.CategoryID = L_Operation.Category.ID;
                L_DOperation.Date = L_Operation.ExecutionDate;
                L_DOperation.ProjectID = L_Operation.Project == null ? null : (Guid?)L_Operation.Project.ID;
                L_DOperation.Description = L_Operation.Description;
                L_DOperation.ID = L_Operation.ID;
                L_DOperation.ValueDate = L_Operation.ValueDate;
                L_DOperation.EndInit();

                Operations.Add(L_DOperation);
            }
        }
    }
}
